package com.junmeng.libadapter.decoration;

import static com.junmeng.libadapter.decoration.GridWrapItemDecoration.UIMode.SPREAD_INSIDE;

import android.graphics.Rect;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

/**
 * 实现item项两种布局样式（与约束布局中的链的样式效果一致,可查看https://developer.android.google.cn/training/constraint-layout#constrain-chain）
 * SPREAD_INSIDE：第一个和最后一个视图固定在链两端的约束边界上，其余视图均匀分布
 * SPREAD：视图是均匀分布的
 * 使用注意：
 * 1 只适用于item项宽度比每个span宽度小的情况
 * 2 目前测试发现不支持约束布局的百分比设置，如RecyclerView设置了layout_constraintWidth_percent
 */
public class GridWrapItemDecoration extends RecyclerView.ItemDecoration {

    //具体效果可以查看[使用 ConstraintLayout 构建自适应界面](https://developer.android.google.cn/training/constraint-layout#constrain-chain)
    @IntDef({UIMode.SPREAD, SPREAD_INSIDE})
    public @interface UIMode {
        int SPREAD = 1;//视图是均匀分布的
        int SPREAD_INSIDE = 2;//第一个和最后一个视图固定在链两端的约束边界上，其余视图均匀分布
    }

    private int mSpanCount;//列数
    private int mVerticalSpace;//垂直间隔，px
    private int mItemWidth;//itemview实际展示的宽度

    @UIMode
    private int mUiMode = SPREAD_INSIDE;

    /**
     * 垂直间隔与计算得到水平间隔一致
     *
     * @param spanCount 列数
     */
    public GridWrapItemDecoration(int spanCount) {
        this(SPREAD_INSIDE, spanCount, -1);
    }

    /**
     * 用户自定义垂直间隔
     *
     * @param spanCount     列数
     * @param verticalSpace 垂直间隔，px
     */
    public GridWrapItemDecoration(int spanCount, int verticalSpace) {
        this(SPREAD_INSIDE, spanCount, verticalSpace);
    }

    public GridWrapItemDecoration(@UIMode int uiMode, int spanCount, int verticalSpace) {
        this.mUiMode = uiMode;
        this.mSpanCount = spanCount;
        this.mVerticalSpace = verticalSpace;
    }

    /**
     * 垂直间隔
     * 动态设置后要生效需手动调用RecyclerView.invalidateItemDecorations();或重新创建设置
     *
     * @param verticalSpace px
     */
    public void setVerticalSpace(int verticalSpace) {
        this.mVerticalSpace = verticalSpace;
    }

    /**
     * 设置垂直间隔并立即生效
     *
     * @param recyclerView
     * @param verticalSpace
     */
    public void setVerticalSpace(@NonNull RecyclerView recyclerView, int verticalSpace) {
        this.mVerticalSpace = verticalSpace;
        recyclerView.invalidateItemDecorations();
    }

    /**
     * 网格的列数
     * 动态设置后要生效需手动调用RecyclerView.invalidateItemDecorations();或重新创建设置
     *
     * @param spanCount
     */
    public void setSpanCount(int spanCount) {
        this.mSpanCount = spanCount;
    }

    /**
     * 设置网格列数并立即生效
     * @param recyclerView
     * @param spanCount
     */
    public void setSpanCount(@NonNull RecyclerView recyclerView, int spanCount) {
        this.mSpanCount = spanCount;
        recyclerView.invalidateItemDecorations();
    }

    /**
     * 设置item的实际宽度，如果没有调用此进行设置则内部会进行自行推算
     * 动态设置后要生效需手动调用RecyclerView.invalidateItemDecorations();或重新创建设置
     * @param itemWidth
     */
    public void setItemWidth(int itemWidth) {
        this.mItemWidth = itemWidth;
    }

    /**
     * 设置item的实际宽度并立即生效
     * @param recyclerView
     * @param itemWidth
     */
    public void setItemWidth(@NonNull RecyclerView recyclerView, int itemWidth) {
        this.mItemWidth = itemWidth;
        recyclerView.invalidateItemDecorations();
    }

    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);


        int recyclerViewWidth = getRestMeasureWidth(parent);//如果使用layout_constraintWidth_percent会发现此时parent还未经过测量，因此为0
        int spanWidth = recyclerViewWidth / mSpanCount;
        int verticalSpace = mVerticalSpace < 0 ? (recyclerViewWidth - mItemWidth * mSpanCount) / (mSpanCount - 1) : mVerticalSpace;//垂直间隔,优先使用用户设置的间隔

        if (mItemWidth <= 0) {
            measure(view, spanWidth, getRestMeasureHeight(parent));
            mItemWidth = view.getMeasuredWidth();
        }
//        LogUtil.i( "recyclerViewWidth=" + recyclerViewWidth + ",spanWidth=" + spanWidth + ",itemWidth=" + mItemWidth + ",verticalSpace=" + verticalSpace);

        int pos = parent.getChildAdapterPosition(view);
        if (pos > mSpanCount - 1) {
            outRect.top = verticalSpace;
        }


        int spanRestWidth = spanWidth - mItemWidth;//每个span宽度减去item宽度之后剩余的宽度
        int posPerRow = pos % mSpanCount;//计算item在每一行的位置，即[0,mSpanCount-1]

        switch (mUiMode) {
            case UIMode.SPREAD_INSIDE:
                outRect.left = posPerRow * spanRestWidth / (mSpanCount - 1);

                //以下写法可能更有助于理解，效果等同上面 outRect.left = posPerRow * spanRestWidth / (mSpanCount - 1);
//                if (posPerRow == 0) {
//                    outRect.left = 0;
//                } else if (posPerRow == mSpanCount - 1) {
//                    outRect.left = spanRestWidth;//将最后一个移到最右边
//                } else {
//                    outRect.left = posPerRow * spanRestWidth / (mSpanCount - 1);//将最后一个移到最右边后多出的宽度平摊给其他
//                }
                break;
            case UIMode.SPREAD:
                outRect.left = (mSpanCount - posPerRow) * spanRestWidth / (mSpanCount + 1);
                break;
        }
//        Log.i("123456","pos="+pos+",mSpanCount="+mSpanCount+",outRect.left="+outRect.left+",mItemWidth="+mItemWidth+",spanWidth="+spanWidth);
    }

    /**
     * 对view进行测量
     *
     * @param view
     * @param restWidth  view的父剩余宽度
     * @param restHeight view的父剩余高度
     */
    public void measure(@NonNull View view, int restWidth, int restHeight) {
        int widthMeasureMode = View.MeasureSpec.EXACTLY;
        int heightMeasureMode = View.MeasureSpec.EXACTLY;
        ViewGroup.LayoutParams lp = view.getLayoutParams();
        if (lp == null) {
            view.measure(View.MeasureSpec.makeMeasureSpec(restWidth, widthMeasureMode),
                    View.MeasureSpec.makeMeasureSpec(restHeight, heightMeasureMode));
            return;
        }
        if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {//WRAP_CONTENT需要AT_MOST的测量模式
            widthMeasureMode = View.MeasureSpec.AT_MOST;
        } else if (lp.width > 0) {
            restWidth = Math.min(lp.width, restWidth);
        }
        if (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {//WRAP_CONTENT需要AT_MOST的测量模式
            heightMeasureMode = View.MeasureSpec.AT_MOST;
        } else if (lp.height > 0) {
            restHeight = Math.min(lp.height, restHeight);
        }
        view.measure(View.MeasureSpec.makeMeasureSpec(restWidth, widthMeasureMode),
                View.MeasureSpec.makeMeasureSpec(restHeight, heightMeasureMode));
    }

    /**
     * 获得指定view的剩余宽度（即可供子View的最大宽度）
     *
     * @param view 此view必须是已经测量过的，否则返回值并不准确
     * @return
     */
    public int getRestMeasureWidth(@NonNull View view) {
        return view.getMeasuredWidth() - view.getPaddingEnd() - view.getPaddingStart();
    }

    /**
     * 获得指定view的剩余高度（即可供子View的最大高度）
     *
     * @param view 此view必须是已经测量过的，否则返回值并不准确
     * @return
     */
    public int getRestMeasureHeight(@NonNull View view) {
        return view.getMeasuredHeight() - view.getPaddingTop() - view.getPaddingBottom();
    }
}
